package cn.jbolt.core.service;

import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.bean.JsTreeBean;
import cn.jbolt.core.common.enums.JBoltSystemLogTargetType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.kit.JBoltUserKit;
import cn.jbolt.core.model.Dept;
import cn.jbolt.core.service.base.JBoltBaseService;
import com.jfinal.aop.Inject;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Okv;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.activerecord.Record;

import java.util.ArrayList;
import java.util.List;
/**
 * 部门service
 * @ClassName: DeptService
 * @author: JFinal学院-小木
 * @date: 2021-02-07 20:34
 */
public class JBoltDeptService extends JBoltBaseService<Dept> {
	protected Dept dao = new Dept().dao();
	@Inject
	protected JBoltUserService jboltUserService;
	@Inject
	protected JBoltDictionaryService jboltDictionaryService;
	@Override
	protected Dept dao() {
		return dao;
	}

  /**
	 * 保存
	 * @param dept
	 * @return
	 */
	public Ret save(Dept dept) {
		if(dept==null ||dept.getPid()==null ||notOk(dept.getName()) || notOk(dept.getSn()) ||notOk(dept.getFullName()) || isOk(dept.getId())) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		if(existsNameWithPid(dept.getName(),dept.getPid())) {return fail(JBoltMsg.DATA_SAME_NAME_EXIST);}
		if(exists("full_name",dept.getSn(), -1 ,dept.getPid())) {return fail(JBoltMsg.DEPT_SAME_FULLNAME_EXIST);}
		if(exists(SN,dept.getSn())) {return fail(JBoltMsg.DEPT_SAME_SN_EXIST);}
		dept.setSortRank(getNextSortRankByPid(dept.getPid()));
		dept.setEnable(true);
		boolean success=dept.save();
		if(success) {
			processDeptPath(dept.getId());
			processParentHasSon(dept.getPid());
			//添加日志
			addSaveSystemLog(dept.getId(), JBoltUserKit.getUserId(), dept.getName());
		}
		return success?successWithData(findById(dept.getId())):fail();
	}

	/**
	 * 更新父节点存在子节点
	 * @param pid
	 */
	private void processParentHasSon(Long pid) {
		if(isOk(pid)){
			updateColumn(pid,"has_son",true);
		}
	}

	/**
	 * 处理deptPath
	 * @return
	 */
	public void processAllDeptPath() {
		Sql sql=selectSql().asc(SORT_RANK).select(ID,SN,PID,"dept_path");
		List<Dept> datas=find(sql);
		if(notOk(datas)) {
			throw new RuntimeException("无任何有效数据");
		}
		List<Dept> depts = convertToModelTree(datas, ID, PID, (p)->notOk(p.getPid()));
		List<Dept> updates = new ArrayList<Dept>();
		for(Dept dept:depts) {
			dept.setDeptPath(dept.getId()+"");
			updates.add(dept);
			if(isOk(dept.getItems())) {
				processItemsDeptPath(dept.getDeptPath(),dept.getItems(),updates);
			}
		}
		batchUpdate(updates);
	}

	private void processItemsDeptPath(String deptPath, List<Dept> items,List<Dept> updates) {
		if(notOk(items)) {return;}
		for(Dept dept:items) {
			dept.setDeptPath(deptPath+KEY_COMMA+dept.getId());
			updates.add(dept);
			if(isOk(dept.getItems())) {
				processItemsDeptPath(dept.getDeptPath(),dept.getItems(),updates);
			}
		}
	}

	/**
	 * 处理deptPath
	 * @param id
	 */
	private void processDeptPath(Long id) {
		Dept dept = findById(id);
		if(dept == null) {
			throw new RuntimeException(JBoltMsg.DATA_NOT_EXIST);
		}
		//有父节点
		if(isOk(dept.getPid())) {
			Dept pdept = findById(dept.getPid());
			if(pdept == null) {
				throw new RuntimeException("处理部门路径时发生异常");
			}
			if(notOk(pdept.getDeptPath())){
				processAllDeptPath();
			}else {
				dept.setDeptPath(pdept.getDeptPath()+KEY_COMMA+id);
				dept.update();
			}
		}else {
			dept.setDeptPath(id+"");
			dept.update();
		}
	}

/**
	 * 保存
	 * @param dept
	 * @return
	 */
	public Ret update(Dept dept) {
		if(dept==null ||dept.getPid()==null ||notOk(dept.getName()) || notOk(dept.getSn()) ||notOk(dept.getFullName())
				|| notOk(dept.getId())) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		if(isOk(dept.getPid())&&dept.getPid().intValue()==dept.getId().intValue()) {
			return fail(JBoltMsg.PARENT_CAN_NOT_SAME_SELF);
		}

		//更新时需要判断数据存在
		Dept dbDept=findById(dept.getId());
		if(dbDept==null) {return fail(JBoltMsg.DATA_NOT_EXIST);}
		if(existsName(dept.getName(), dept.getId(),dept.getPid())) {return fail(JBoltMsg.DATA_SAME_NAME_EXIST);}
		if(exists("full_name",dept.getFullName(), dept.getId(),dept.getPid())) {return fail(JBoltMsg.DEPT_SAME_FULLNAME_EXIST);}
		if(exists(SN,dept.getSn(), dept.getId())) {return fail(JBoltMsg.DEPT_SAME_SN_EXIST);}
		boolean changeParent=dbDept.getPid().intValue()!=dept.getPid().intValue();
		if(changeParent) {
			dept.setSortRank(getNextSortRankByPid(dept.getPid()));
		}

		boolean success=dept.update();
		if(success) {
			if(changeParent) {
				updateSortRankAfterChangeParentNode(Okv.by(PID, dbDept.getPid()),dbDept.getSortRank());
				processDeptPath(dept.getId());
				processParentHasSon(dept.getPid());
				processCheckAndSetNodeHasSon(dbDept.getPid());
			}
			//添加日志
			addUpdateSystemLog(dept.getId(), JBoltUserKit.getUserId(),  dept.getName());
		}
		return success?successWithData(findById(dept.getId())):fail();
	}

	private void processCheckAndSetNodeHasSon(Long id) {
		if(isOk(id)){
			boolean hasSon = existsSon(id);
			updateColumn(id,"has_son",hasSon);
		}
	}

	/**
	 * 得到JSTree数据源
	 * @param checkedId 默认选中节点
	 * @param openLevel -1全部 0 不动 >0指定层级
	 * @return
	 */
	public List<JsTreeBean> getEnableJsTree(Long checkedId,int openLevel) {
		List<Dept> depts=getEnableList();
		return convertJsTree(depts,checkedId,openLevel,SORT_RANK);
	}
	/**
	 * 得到JSTree数据源
	 * @param checkedId 默认选中节点
	 * @param openLevel -1全部 0 不动 >0指定层级
	 * @return
	 */
	public List<JsTreeBean> getAllJsTreeDatas(Long checkedId,int openLevel) {
		List<Dept> depts=getAllList();
		return convertJsTree(depts,checkedId,openLevel,SORT_RANK);
	}
	/**
	 * 获取enable=true的数据
	 * @return
	 */
	public List<Dept> getEnableList() {
		return find(selectSql().eq(ENABLE, TRUE).asc(SORT_RANK));
	}

	/**
	 * 获取所有数据
	 * @return
	 */
	public List<Dept> getAllList() {
		return find(selectSql().asc(SORT_RANK));
	}

	@Override
	protected String afterDelete(Dept dept, Kv kv) {
		//添加日志
		addDeleteSystemLog(dept.getId(), JBoltUserKit.getUserId(), dept.getName());
		//看看父节点是否需要处理hasSon
		processCheckAndSetNodeHasSon(dept.getPid());
		return null;
	}

	/**
	 * 是否是否可以切换指定boolean字段
	 * @param model
	 * @param column
	 * @param kv
	 * @return
	 */
	@Override
	public String 	checkCanToggle(Dept model, String column, Kv kv) {
		return null;
	}

	/**
	 * 检测是否可以删除
	 * @param model
	 * @param kv    额外参数
	 * @return
	 */
	@Override
	public String checkCanDelete(Dept model, Kv kv) {
		return checkInUse(model, kv);
	}

	/**
	 * 检测是否被使用
	 * @param dept
	 * @param kv    额外参数
	 * @return
	 */
	@Override
	public String checkInUse(Dept dept, Kv kv) {
		boolean existSons=existsSon(dept.getId());
		if(existSons) {
			return JBoltMsg.EXIST_SON;
		}

		boolean userUse=jboltUserService.checkDeptInUse(dept.getId());
		if(userUse) {
			return JBoltMsg.DEPT_USER_USE;
		}

		return null;
	}

	/**
	 * 获取树形结构数据
	 * @return
	 */
	public List<Dept> getTreeDatas(boolean onlyEnable,boolean asOptions){
		Sql sql=selectSql().asc(SORT_RANK);
		if(asOptions) {
			sql.select(ID,NAME,PID,SN);
		}
		if(onlyEnable) {
			sql.eq(ENABLE, TRUE);
		}
		List<Dept> datas=find(sql);
		return convertToModelTree(datas, ID, PID, (p)->notOk(p.getPid()));
	}
	/**
	 * 获取树形结构数据
	 * @param enable
	 * @return
	 */
	public List<Record> getTreeTableDatas(Boolean enable){
		Sql sql=selectSql().asc("dept.sort_rank").from(table(),"dept");
//		sql.select("dept.*","dic.name as type_name");
		sql.select("dept.*","IF(dept.enable = '1', true, false) AS enable,IF(dept.has_son = '1', true, false) AS has_son","dic.name as type_name");
		sql.leftJoin(jboltDictionaryService.table(), "dic", "dept.type=dic.sn");
		sql.eq("dic.type_key", "dept_type");
		sql.enableEq(enable);
		List<Record> datas=findRecord(sql,true);
		return convertToRecordTree(datas, ID, PID, (p)->notOk(p.getLong(PID)));
	}

	@Override
	protected String afterToggleBoolean(Dept dept, String column, Kv kv) {
		//添加日志
		if(dept.getEnable()) {
			processParentEnableTrue(dept.getPid());
		}else {
			processSonEnableFalse(dept.getId());
		}
		addUpdateSystemLog(dept.getId(), JBoltUserKit.getUserId(),dept.getName(),"的启用状态:"+dept.getEnable());
		return null;
	}

	/**
	 * 处理所有父节点enable=true
	 * @param pid
	 */
	private void processParentEnableTrue(Long pid) {
		if(isOk(pid)) {
			Dept dept=findById(pid);
			if(dept!=null) {
				if(!dept.getEnable()) {
					dept.setEnable(true);
					dept.update();
					processParentEnableTrue(dept.getPid());
				}
			}
		}
	}

	/**
	 * 处理所有子节点enable=false
	 * @param id
	 */
	private void processSonEnableFalse(Long id) {
		dao().each(son->{
			son.setEnable(false);
			son.update();
			processSonEnableFalse(son.getId());
			return true;
		}, selectSql().pidEqQM().eq(ENABLE,TRUE).toSql(), id);
	}

/**
	  * 上移
	 * @param id
	 * @return
	 */
	public Ret up(Long id) {
		Dept dept=findById(id);
		if(dept==null){
			return fail("数据不存在或已被删除");
		}
		Integer rank=dept.getSortRank();
		if(rank==null||rank<=0){
			return fail("顺序需要初始化");
		}
		if(rank==1){
			return fail("已经是第一个");
		}
		Dept upDept=findFirst(Okv.by(SORT_RANK, rank-1).set(PID,dept.getPid()));
		if(upDept==null){
			return fail("顺序需要初始化");
		}
		upDept.setSortRank(rank);
		dept.setSortRank(rank-1);

		upDept.update();
		dept.update();
		return SUCCESS;
	}

/**
	 * 下移
	 * @param id
	 * @return
	 */
	public Ret down(Long id) {
		Dept dept=findById(id);
		if(dept==null){
			return fail("数据不存在或已被删除");
		}
		Integer rank=dept.getSortRank();
		if(rank==null||rank<=0){
			return fail("顺序需要初始化");
		}
		int max=getCount();
		if(rank==max){
			return fail("已经是最后一个");
		}
		Dept upDept=findFirst(Okv.by(SORT_RANK, rank+1).set(PID,dept.getPid()));
		if(upDept==null){
			return fail("顺序需要初始化");
		}
		upDept.setSortRank(rank);
		dept.setSortRank(rank+1);

		upDept.update();
		dept.update();
		return SUCCESS;
	}



	private void initOneRank(List<Dept> depts) {
		if(notOk(depts)){
			return;
		}
		for(int i=0;i<depts.size();i++){
			depts.get(i).setSortRank(i);
		}
		batchUpdate(depts);
		for(Dept dept:depts){
			//批量更新里已经处理了cache相关删除工作
//			deleteCacheById(dept.getId());
//			deleteCacheByKey(dept.getSn());
			//继续向下递归
			List<Dept> items=getSonsOrderById(dept.getId());
			initOneRank(items);

		}

	}

	/**
	 * 初始化排序
	 * @return
	 */
	public Ret initRank(){
		List<Dept> parents=getAllParentsOrderById();
		if(parents.size()>0){
			initOneRank(parents);
		}
		//添加日志
		addUpdateSystemLog(null, JBoltUserKit.getUserId(),"全部","初始化顺序");
		return SUCCESS;

	}

	public List<Long> processSonDeptIds(Long deptId) {
		List<Long> ids=new ArrayList<Long>();
		if(isOk(deptId)) {
			processSonDeptIds(ids, deptId);
		}
		return ids;
	}
	private void processSonDeptIds(List<Long> ids, Long deptId) {
		if(isOk(deptId)) {
			dao().each(son->{
				ids.add(son.getId());
				processSonDeptIds(ids, son.getId());
				return true;
			}, selectSql().pidEqQM().toSql(), deptId);
		}
	}

	/**
	 *
	 * 树节点移动位置
	 * @param id
	 * @param newPid
	 * @param newRank
	 * @return
	 */
	public Ret move(Long id, Long newPid, Integer newRank) {
		Dept old=findById(id);
		if(old==null){return fail(JBoltMsg.DATA_NOT_EXIST);}
		Long oldPid=old.getPid();
		Integer oldRank=old.getSortRank();
		boolean needChangePid = false;
		//原来的PID 等于现在的PID 说明是在一个父节点下移动 就切换sortRank处理即可
		if(oldPid.intValue()==newPid.intValue()){
			List<Dept> others = null;
//			Okv paras=Okv.by("pid =", oldPid).set("sort_rank >=", newRank).set("id <>", id);
//			List<Dept> others=getCommonList("*", paras, SORT_RANK, "asc", true);
			//如果没有改变父亲 只调整了顺序
			if(newRank.intValue()<oldRank){
				others = getSameParentNewAfterSons(oldPid,newRank,id);
				//向左调 就修改新位置向右的数据 sort_rank+1
				doUpdateRightRanks(others,newRank+1);
			}else{
				others = getSameParentNewBeforeSons(oldPid,oldRank,newRank,id);
				//向右调整顺序
				doUpdateBetweenRanks(others,oldRank);
			}

		}else{
			//如果不等于原来的pid
			//1、重新排列原来的PID下的数据
			List<Dept> samePidRightSons =  getSameParentNewAfterSons(oldPid,oldRank,id);
//			Okv allparas=Okv.by("pid =", oldPid).set("id <>", id);
//			List<Dept> all=getCommonList("*", allparas, SORT_RANK, "asc", true);
//			doUpdateRanks(all);
			doUpdateRightRanks(samePidRightSons,oldRank);
			//2、新的里面从新位置往后排列
			//新位置右侧调整顺序
//			Okv otherparas=Okv.by("pid =", newPid).set("sort_rank >=", newRank).set("id <>", id);
//			List<Dept> others=getCommonList("*", otherparas, SORT_RANK, "asc", true);
			List<Dept> others = getSameParentNewAfterSons(newPid,newRank,id);
			doUpdateRightRanks(others,newRank+1);
			needChangePid = true;
		}
		if(needChangePid){
			old.setPid(newPid);
		}
		old.setSortRank(newRank);
		//最后更自己
		boolean success = old.update();
		if(success){
			//处理deptPath
			processDeptPathWithLeafNode(old.getId());
			//添加日志
			addUpdateSystemLog(old.getId(), JBoltUserKit.getUserId(), old.getName(),"位置和顺序");
		}
		return SUCCESS;
	}

	private void doUpdateBetweenRanks(List<Dept> others, Integer oldRank) {
		for(int i=0;i<others.size();i++){
			others.get(i).setSortRank(oldRank+i);
		}
		batchUpdate(others);
	}

	/**
	 * 获取同一个父节点下指定节点移入后位置到移入前位置中间的sons
	 * @param pid
	 * @param oldRank
	 * @param newRank
	 * @param id
	 * @return
	 */
	private List<Dept> getSameParentNewBeforeSons(Long pid, Integer oldRank, Integer newRank, Long id) {
		return find(selectSql().eq(PID,pid).ge(SORT_RANK,oldRank+1).le(SORT_RANK,newRank).idNotEq(id).asc(SORT_RANK));
	}

	/**
	 * 获取同一个父节点下指定节点移入后位置向后的子节点数据list
	 * @param pid
	 * @param newRank
	 * @param id
	 * @return
	 */
	private List<Dept> getSameParentNewAfterSons(Long pid, Integer newRank, Long id) {
		return find(selectSql().eq(PID,pid).ge(SORT_RANK,newRank).idNotEq(id).asc(SORT_RANK));
	}


	/**
	 * 递归处理
	 * @param id
	 */
	private void processDeptPathWithLeafNode(Long id) {
		processDeptPath(id);
		List<Dept> sons = getSons(id);
		for(Dept son:sons){
			processDeptPathWithLeafNode(son.getId());
		}
	}

	private void doUpdateRanks(List<Dept> others) {
		for(int i=0;i<others.size();i++){
			others.get(i).setSortRank(i);
		}
		batchUpdate(others);
	}

	/**
	 * 处理更新右侧的rank
	 * @param others
	 * @param startRank
	 */
	private void doUpdateRightRanks(List<Dept> others, int startRank) {
		if(others.size()>0){
			for(Dept category:others){
				category.setSortRank(startRank);
				startRank=startRank+1;
			}
			batchUpdate(others);
		}

	}

	@Override
	protected int systemLogTargetType() {
		return JBoltSystemLogTargetType.DEPT.getValue();
	}

}
